package com.dd;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;

import com.chantel.tool.db.DBConnMgr;

/**
 * 用来自动生成唯一ID
 */
public class SequenceManager {

	private static final Logger logger = Logger.getLogger(	SequenceManager.class.getName());
    private static final String LOAD_ID =
        "SELECT id FROM libID";
    private static final String UPDATE_ID =
        "UPDATE libID SET id=? WHERE idType=? AND id=?";

    /**
     * The number of ID's to checkout at a time. 15 should provide a good
     * balance between speed and not wasing too many ID's on appserver restarts.
     * Feel free to change this number if you believe your Jive setup warrants
     * it.
     */
    private static final int INCREMENT = 3;
    
    
    
    // Statically startup a sequence manager for each of the five sequence
    // counters.
    private static SequenceManager[] managers;
    static {
        managers = new SequenceManager[SysGlobals.globalsFiledCount];
        for (int i=0; i<managers.length; i++) {
            managers[i] = new SequenceManager(i);
        }
    }

    /**
     * Returns the next ID of the specified type.
     *
     * @param type the type of unique ID.
     * @return the next unique ID of the specified type.
     */
    public static int nextID(int type) {
        return managers[type].nextUniqueID();
    }

    public static int nextID() {
        return managers[0].nextUniqueID();
    }

    private int type;
    private int currentID;
    private int maxID;

    /**
     * Creates a new DbSequenceManager.
     */
    public SequenceManager(int type) {
        this.type = type;
        currentID = 0;
        maxID = 0;
    }

    /**
     * Returns the next available unique ID. Essentially this provides for the
     * functionality of an auto-increment database field.
     */
    public synchronized int nextUniqueID() {
        if (! (currentID < maxID)) {
            // Get next block -- make 5 attempts at maximum.
            getNextBlock(5);
        }
        int id = currentID;
        currentID++;
        return id;
    }

    /**
     * Performs a lookup to get the next availabe ID block. The algorithm is as
     * follows:<ol>
     *  <li> Select currentID from appropriate db row.
     *  <li> Increment id returned from db.
     *  <li> Update db row with new id where id=old_id.
     *  <li> If update fails another process checked out the block first; go
     *          back to step 1. Otherwise, done.
     * </ol>
     */
    private void getNextBlock(int count) {
        if (count == 0) {
            System.err.println("Failed at last attempt to obtain an ID, aborting...");
            return;
        }
        boolean success = false;
        Connection con = null;
        PreparedStatement pstmt = null;
        try {
            /*con = ConnectionManager.getConnection();
            // Get the current ID from the database.
            pstmt = con.prepareStatement(LOAD_ID);
            pstmt.setInt(1, type);
            //System.out.println("type:"+type);
            ResultSet rs = pstmt.executeQuery();*/
        	DBUtil2 du2 = new DBUtil2();
        	Map whereFields = new HashMap();
        	whereFields.put("idType", new Integer(this.type));
        	Map results = du2.getRow(LOAD_ID, whereFields);
            if (results.size()<1) {
                throw new SQLException("Loading the current ID failed. The " +
                    "jiveID table may not be correctly populated.");
            }
            int currentID = ( (Integer)results.get("id") ).intValue();
            
            
            if(pstmt != null)
            {
            	pstmt.close();
            }          
            
            // Increment the id to define our block.
            int newID = currentID + INCREMENT;
            // The WHERE clause includes the last value of the id. This ensures
            // that an update will occur only if nobody else has performed an
            // update first.
            DBConnMgr dbm = new DBConnMgr();
            con = dbm.getConnection();
            pstmt = con.prepareStatement(UPDATE_ID);
            pstmt.setInt(1, newID);
            pstmt.setInt(2, type);
            pstmt.setInt(3, currentID);
            // Check to see if the row was affected. If not, some other process
            // already changed the original id that we read. Therefore, this
            // round failed and we'll have to try again.
            success = pstmt.executeUpdate() == 1;
            if (success) {
                this.currentID = currentID;
                this.maxID = newID;
            }
        }
        catch( Exception sqle ) {
            sqle.printStackTrace();
        }
        finally {
            try {  pstmt.close();   }
            catch (Exception e) { e.printStackTrace(); }
            try {  con.close();   }
            catch (Exception e) { e.printStackTrace(); }
        }
        if (!success) {
            System.err.println("WARNING: failed to obtain next ID block due to " +
                "thread contention. Trying again...");
            // Call this method again, but sleep briefly to try to avoid thread
            // contention.
            try {
                Thread.currentThread().sleep(75);
            } catch (InterruptedException ie) { }
            getNextBlock(count-1);
        }
    }
}
